﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Web;
using System.Web.Mvc;
using IrisMembership.DomainClasses;
using StructureMap;
using IrisMembership.ServiceLayer;

namespace IrisMembership.Web.Authentication
{
    public class IrisAuthorize : FilterAttribute, IAuthorizationFilter
    {
        public string AreaName { get; set; }

        private readonly object _typeId = new object();

        public override object TypeId
        {
            get { return _typeId; }
        }
        public void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
            {
                // If a child action cache block is active, we need to fail immediately, even if authorization
                // would have succeeded. The reason is that there's no way to hook a callback to rerun
                // authorization before the fragment is served from the cache, so we can't guarantee that this
                // filter will be re-run on subsequent requests.
                throw new InvalidOperationException("AuthorizeAttribute_CannotUseWithinChildActionCache");
            }

            bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
                                     || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);

            if (skipAuthorization)
            {
                return;
            }

            if (AuthorizeCore(filterContext))
            {
                // ** IMPORTANT **
                // Since we're performing authorization at the action level, the authorization code runs
                // after the output caching module. In the worst case this could allow an authorized user
                // to cause the page to be cached, then an unauthorized user would later be served the
                // cached page. We work around this by telling proxies not to cache the sensitive page,
                // then we hook our custom authorization code into the caching mechanism so that we have
                // the final say on whether a page should be served from the cache.

                HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
                cachePolicy.SetProxyMaxAge(new TimeSpan(0));
                cachePolicy.AddValidationCallback(CacheValidateHandler, filterContext /* data */);
            }
            else
            {
                HandleUnauthorizedRequest(filterContext);
            }
        }

        protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
            filterContext.Result = new HttpUnauthorizedResult();
        }

        // This method must be thread-safe since it is called by the caching module.
        protected virtual HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext, AuthorizationContext filterContext)
        {
            if (httpContext == null)
            {
                throw new ArgumentNullException("httpContext");
            }

            bool isAuthorized = AuthorizeCore(filterContext);
            return (isAuthorized) ? HttpValidationStatus.Valid : HttpValidationStatus.IgnoreThisRequest;
        }

        protected virtual bool AuthorizeCore(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            var user = filterContext.HttpContext.User as IrisPrincipal;

            if (!user.Identity.IsAuthenticated)
            {
                return false;
            }



            var userName = user.Information.UserId;


            var actionName = filterContext.ActionDescriptor.ActionName;

            var controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;


            var areaName =
                (filterContext.ActionDescriptor.ControllerDescriptor.GetCustomAttributes(true)
                    .Where(x => x.GetType() == typeof(IrisAuthorize)).First() as IrisAuthorize).AreaName;



            var permissionService = ObjectFactory.GetInstance<IPermissionService>();



            return permissionService.CanAccess(user.Information.UserId, controllerName, actionName, areaName);

        }

        private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
        {
            validationStatus = OnCacheAuthorization(new HttpContextWrapper(context), (AuthorizationContext)data);
        }

        private void loadTreeNode(ICollection<Role> treeRoles, ICollection<Role> listRoles)
        {
            foreach (Role roleNode in treeRoles)
            {
                //Trace.WriteLine(String.Format(node.Tag));
                listRoles.Add(roleNode);
                loadTreeNode(roleNode.ChildrenRoles, listRoles);
            }
        }





    }
}